package com.hech.hmall.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hech.hmall.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;

/**
 * @author 韩家丰
 * @date 2021/5/3 16:35
 * @desc token工具类, 用于生成、校验、保存token
 */
@Component
@Slf4j
public class TokenHelper {

    /**
     * 密钥
     */
    private final byte[] secret = "PZ6wZ7bKY2V4fLSTOrIutK6NG4bfOrCGridAngQlLibSk0wj0g3M0B7qxOsGZUhCsiIktG3anQATNyiNl2VKls8Xsf4s9yPs".getBytes(StandardCharsets.UTF_8);
    /**
     * 指定算法
     */
    private final Algorithm algorithm = Algorithm.HMAC256(secret);
    private final RedisUtil redisUtil;
    /**
     * token过期时间24小时
     */
    private final Long expired_time = 1000L * 60 * 60 * 24;

    public TokenHelper(RedisUtil redisUtil) {
        this.redisUtil = redisUtil;
    }

    /**
     * 使用id和用户名生成token
     *
     * @param userEntity 用户
     * @return token
     */
    public String createToken(UserEntity userEntity) {
        String token = JWT.create()
                .withClaim("id", userEntity.getId())
                .withClaim("account", userEntity.getAccount())
                .withExpiresAt(new Date(System.currentTimeMillis() + expired_time))
                .withIssuer("auth0")
                .sign(algorithm);
        log.info("[创建token]用户(" + userEntity.getAccount() + ")token创建成功:" + token);
        //存入redis
        saveToken(token, userEntity);
        return token;
    }

    /**
     * 校验token,成功返回User
     *
     * @param token 待校验token
     * @return user user
     * @throws JWTVerificationException token校验异常
     */
    public UserEntity verifyToken(String token) throws JWTVerificationException {
        if (Objects.isNull(token)) {
            throw new JWTVerificationException("token is null");
        }
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build();
        DecodedJWT jwt = verifier.verify(token);
        UserEntity userEntity = (UserEntity) redisUtil.get(token);
        if (Objects.isNull(userEntity)) {
            log.error("[校验token]token校验失败,redis中不存在该token");
            throw new JWTVerificationException("token校验失败");
        }
        Integer userId = jwt.getClaim("id").asInt();
        String userName = jwt.getClaim("account").asString();
        if (!(userId.equals(userEntity.getId()) && userName.equals(userEntity.getAccount()))) {
            log.error("[校验token]token校验失败,token和redis中存放的用户信息不一致");
            throw new JWTVerificationException("token校验失败");
        }
        log.info(String.format("[校验token]校验通过,token:%s", token));
        return userEntity;
    }

    /**
     * 重新生成token
     *
     * @param oldToken 原先token
     * @return 新token
     */
    public String regenerateToken(String oldToken) {
        //从redis中取出user
        UserEntity userEntity = (UserEntity) redisUtil.get(oldToken);
        if (userEntity == null) {
            throw new JWTVerificationException("[校验token]token校验失败");
        }
        //删除原先redis中存储的token
        redisUtil.del(oldToken);
        //生成新的token存入redis
        return createToken(userEntity);
    }

    /**
     * 使用token为键,user为值存入redis,指定过期时间
     *
     * @param token      token
     * @param userEntity user
     */
    public void saveToken(String token, UserEntity userEntity) {
        redisUtil.set(token, userEntity, expired_time);
        log.info(String.format("[存储token]token已存入redis,过期时间%dms,token:%s", expired_time, token));
    }
}
